//DEFINED
//OBSERVATION_VECTOR_SIZE, NUMBER_OF_OBSERVATIONS,NUMBER_OF_CENTROIDS,
//KERNEL_WORK_ITEM_OBSERVATIONS_PROCESSED,KERNEL_WORK_ITEM_CENTROIDSS_PROCESSED

float calculateDistanceEst(const float* _x1, const float* _x2)
	{
		float returned=0;
		for(int i=0;i<OBSERVATION_VECTOR_SIZE;i++,_x1+=NUMBER_OF_OBSERVATIONS,_x2+=NUMBER_OF_OBSERVATIONS)
		{
			float diff_=(*_x1)-(*_x2);
			returned+=diff_*diff_;
		}
		return returned;
	}

void calculateSum(float* _output,const float* _x1,const float* _x2)
	{
		for(int i=0;i<OBSERVATION_VECTOR_SIZE;i++,_output++,_x1++,_x2++)
		{
			(*_output)=(*_x1)+(*_x2);
		}

	}

void calculateMultiply(float* _output,const float* _InputVec,const float _InputScalar)
	{
		for(int i=0;i<OBSERVATION_VECTOR_SIZE;i++,_output++,_InputVec++)
		{
			(*_output)=(*_InputVec)*(_InputScalar);
		}

	}

void calculateAssignment(__global float* _output,const float* _InputVec)
	{
		for(int i=0;i<OBSERVATION_VECTOR_SIZE;i++,_output++,_InputVec++)
		{
			(*_output)=(*_InputVec);
		}

	}


bool observationVectorElementsEqual(const float* _x1, const float* _x2)
	{
		return (_x1-_x2<=DATA_POINC_VECTOR_ELEMENT_EPSILON)
			&&(_x2-_x1<=DATA_POINC_VECTOR_ELEMENT_EPSILON);
	}

bool calculateEqual(const float* _x,const float* _y)
	{
		for(int i=0;i<OBSERVATION_VECTOR_SIZE;i++,_x++,_y++)
		{
			if(!(observationVectorElementsEqual(_x,_y)))return false;
		}
		return true;
	}

void assigningEachObservationToTheClosestCentroid(__global float* _observations,
                             __global float* _currentCentroids,
                             __global unsigned int* _closestCentroid,
							 __local float* _localCentroid)
{
	for(int observationIndex=get_global_id(0);observationIndex<NUMBER_OF_OBSERVATIONS;observationIndex+=get_global_size(0))
	{
		

		float observation[OBSERVATION_VECTOR_SIZE];
		for(int observationVectorIndex=0;observationVectorIndex<OBSERVATION_VECTOR_SIZE;observationVectorIndex++)
		{
			observation[observationVectorIndex]=*(_observations+observationIndex+(observationVectorIndex*NUMBER_OF_OBSERVATIONS));
		}

		float minDist_=MAXFLOAT;
		unsigned int minI_=0;

		for(int centroidIndex=0;centroidIndex<NUMBER_OF_CENTROIDS;centroidIndex++)
		{
			if (get_local_id(0) == 0) 
			{
				
				for(int centroidVectorIndex=0;centroidVectorIndex<OBSERVATION_VECTOR_SIZE;centroidVectorIndex++)
				{
					_localCentroid[centroidVectorIndex]=*(_currentCentroids+centroidIndex+(centroidVectorIndex*NUMBER_OF_CENTROIDS));
				}
			}
			barrier(CLK_LOCAL_MEM_FENCE);

			float centroid[OBSERVATION_VECTOR_SIZE];
			for(int centroidVectorIndex=0;centroidVectorIndex<OBSERVATION_VECTOR_SIZE;centroidVectorIndex++)
				{
					centroid[centroidVectorIndex]=_localCentroid[centroidVectorIndex];
				}

			float currentDist_=calculateDistanceEst(observation,centroid);
			if(currentDist_<minDist_)
			{
				minDist_=currentDist_;
				minI_=centroidIndex;
			}

		}
	_closestCentroid[observationIndex]=minI_;

	}
}

void recalculateCentroids(__global float* _observations,
                             __global float* _currentCentroids,
                             __global unsigned int* _closestCentroid,
							 __local float* _localObservation)
{
	for(int centroidIndex=get_global_id(0);centroidIndex<NUMBER_OF_CENTROIDS;centroidIndex+=get_global_size(0))
	{
		float centroid[OBSERVATION_VECTOR_SIZE];
		for(int centroidVectorIndex=0;centroidVectorIndex<OBSERVATION_VECTOR_SIZE;centroidVectorIndex++)
		{
			centroid[centroidVectorIndex]=*(_currentCentroids+centroidIndex+(centroidVectorIndex*NUMBER_OF_CENTROIDS));
		}

		float observationsCount=0;
		float observationsSum[OBSERVATION_VECTOR_SIZE]={0};

		for(int observationIndex=0;observationIndex<NUMBER_OF_OBSERVATIONS;observationIndex++)
		{
			if (get_local_id(0) == 0) 
			{
				
				for(int observationVectorIndex=0;observationVectorIndex<OBSERVATION_VECTOR_SIZE;observationVectorIndex++)
				{
					_localObservation[observationVectorIndex]=*(_observations+observationIndex+(observationVectorIndex*NUMBER_OF_OBSERVATIONS));
				}
			}

			unsigned int closestCentroidIndex=_closestCentroid[observationIndex];
			barrier(CLK_LOCAL_MEM_FENCE);

			float observation[OBSERVATION_VECTOR_SIZE];
			for(int observationVectorIndex=0;observationVectorIndex<OBSERVATION_VECTOR_SIZE;observationVectorIndex++)
				{
					observation[observationVectorIndex]=_localObservation[observationVectorIndex];
				}

			if(closestCentroidIndex==centroidIndex)
			{
				observationsCount++;
				calculateSum(observationsSum,observationsSum,observation);
			}

		}
		calculateMultiply(observationsSum,observationsSum,
					(1.0f)/observationsCount);//?? divide by zero;
		calculateAssignment(_currentCentroids+centroidIndex,observationsSum);

		if(calculateEqual(observationsSum,centroid))
		{
		}

	}
}

bool convergenceTest(__global float* _observations,
                             __global float* _currentCentroids,
                             __global unsigned int* _closestCentroid)
{
	return true;//??
}


__kernel void kMeans(__global float* _observations,
                             __global float* _currentCentroids,
                             __global unsigned int* _closestCentroid)
{
	__local float localObservation[OBSERVATION_VECTOR_SIZE];
	__local float localCentroid[OBSERVATION_VECTOR_SIZE];

	for(;;)
	{
		assigningEachObservationToTheClosestCentroid(_observations,_currentCentroids,_closestCentroid,localCentroid);
		recalculateCentroids(_observations,_currentCentroids,_closestCentroid,localObservation);
		if(!convergenceTest(_observations,_currentCentroids,_closestCentroid))break;
		break;//??
	}
}